package edu.ssdut.revise.models;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class VisClassOrInterfaceNode extends VisBaseNode {
    private final String identifier;
    private final Boolean isInterface;
    private final List<VisClassOrInterfaceNode> classOrInterfaces = new ArrayList<>();
    private final List<VisEnumNode> enums = new ArrayList<>();
    private final List<VisMethodNode> methods;
    private final List<VisConstructorNode> constructors;
    private final List<VisFieldNode> fields;          //note that here wo split variables in one field into fields; i.e. int a,b;
    private final List<String> typeParameters;  //TODO:使用字符串List表示泛型，不是很好的解决方案
    private final List<String> modifiers;
    private final List<String> annotations;

    private final String extendedClassName;     //TODO:改为节点？（不同文件间） class和接口用
    private final List<String> implementedInterfaceNames;

    public VisClassOrInterfaceNode(ClassOrInterfaceDeclaration cd) {
        super(cd);

        this.typeParameters = cd.getTypeParameters().stream().map(Object::toString).collect(Collectors.toList());
        this.identifier = cd.getNameAsString();
        this.isInterface = cd.isInterface();
        cd.getMembers().forEach(member -> {
            if (member instanceof ClassOrInterfaceDeclaration) {
                this.classOrInterfaces.add(new VisClassOrInterfaceNode((ClassOrInterfaceDeclaration) member));
            } else if (member instanceof EnumDeclaration) {
                this.enums.add(new VisEnumNode((EnumDeclaration) member));
            }
        });
        this.methods = cd.getMethods().stream().map(VisMethodNode::new).collect(Collectors.toList());
        this.constructors = cd.getConstructors().stream().map(VisConstructorNode::new).collect(Collectors.toList());
        this.modifiers = cd.getModifiers().stream().map(Node::toString).collect(Collectors.toList());
        this.annotations = cd.getAnnotations().stream().map(Objects::toString).collect(Collectors.toList());
        this.extendedClassName = cd.getExtendedTypes().size() == 1 ? cd.getExtendedTypes(0).getNameAsString() : "";   //TODO：why there are multiple extendedTypes? for multi-layer extenting?
        this.implementedInterfaceNames = cd.getImplementedTypes().stream().map(NodeWithSimpleName::getNameAsString).collect(Collectors.toList());
        this.fields = cd.getFields().stream().map(fd -> fd.getVariables().stream()
                        .map(v -> new VisFieldNode(v,
                                fd.getAnnotations().stream().map(Objects::toString).collect(Collectors.toList()),
                                fd.getModifiers().stream().map(Objects::toString).collect(Collectors.toList())
                        )).collect(Collectors.toList()))
                .flatMap(Collection::stream).collect(Collectors.toList());
    }

    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder(isInterface ? "Interface" : "Class" + " " + this.identifier + System.lineSeparator() +
                "extends: " + this.extendedClassName + System.lineSeparator() +
                "implements: " + this.implementedInterfaceNames.toString() + System.lineSeparator() +
                "typeParameters: " + this.typeParameters + System.lineSeparator());
        for (VisFieldNode field : fields) {
            ret.append(field.toString());
        }
        for (VisMethodNode method : methods) {
            ret.append(method.toString());
        }
        ret.append("Modifiers: ").append(modifiers.toString()).append(System.lineSeparator());
        ret.append("Annotations: ").append(annotations.toString()).append(System.lineSeparator());
        return ret.toString();
    }

    @Override
    public String getTypeName() {
        return "ClassOrInterfaceNode";
    }

    public String getIdentifier() {
        return identifier;
    }

    public Boolean getIsInterface() {
        return isInterface;
    }

    public List<VisMethodNode> getMethods() {
        return methods;
    }

    public List<VisConstructorNode> getConstructors() {
        return constructors;
    }

    public List<VisFieldNode> getFields() {
        return fields;
    }

    public List<String> getTypeParameters() {
        return typeParameters;
    }

    public List<String> getModifiers() {
        return modifiers;
    }

    public List<String> getAnnotations() {
        return annotations;
    }

    public String getExtendedClassName() {
        return extendedClassName;
    }

    public List<String> getImplementedInterfaceNames() {
        return implementedInterfaceNames;
    }

    public List<VisClassOrInterfaceNode> getClassOrInterfaces() {
        return classOrInterfaces;
    }

    public List<VisEnumNode> getEnums() {
        return enums;
    }
}
